package com.koudai.finance.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import org.apache.commons.codec.binary.Base64;


/** 
 * Description  
 * 云账户签名
 *
 * @version 1.0
 * @date 2019/12/23 20:03
 */
@Slf4j
public class SignUtil {

    /**
     * 数字签名，密钥算法
     */
    public static final String KEY_ALGORITHM = "RSA";
    /**
     * 数字签名 签名/验证算法
     */
    public static final String SIGNATURE_MD5 = "MD5withRSA";
    public static final String SIGNATURE_SHA = "SHA256withRSA";

    /**
     * 加签
     *
     * @param json       业务报文
     * @param privatekey 私钥
     * @return 加签后的签值
     * @throws Exception
     */
    public static String sign(JSONObject json, String privatekey) throws Exception {
        // json格式转换，以保证是json格式
        json = JSONObject.parseObject(json.toJSONString());
        Map<String, String> map = new HashMap<String, String>();
        getNeedMap(json, map);
        System.out.println("深度提取的报文：" + JSONObject.toJSONString(map));
        Map<String, String> resultMap = sortMapByKey(map);
        System.out.println("排序后的报文：" + JSONObject.toJSONString(resultMap));
        String content = getSignString(resultMap);
        System.out.println("拼接后的报文：" + JSONObject.toJSONString(content));
        byte[] data = content.getBytes(StandardCharsets.UTF_8);
        byte[] keyBytes = new Base64().decode(privatekey);
        return sign(data, keyBytes);
    }

    /**
     * server渠道验签
     *
     * @param resultMap 报文对象
     * @param publicKey 公钥
     * @return 验签成功返回true，反之false
     * @throws Exception
     */
    public static boolean verify(Map<String, Object> resultMap, String publicKey) throws Exception {
        String sign = (String) resultMap.get("sign");
        resultMap.remove("sign");
        byte[] data = null;
        boolean ret;

        JSONObject json = new JSONObject(resultMap);
        Map<String, String> map = new HashMap<String, String>();
        getNeedMap(json, map);
        Map<String, String> sortMap = sortMapByKey(map);
        data = getSignString(sortMap).getBytes(StandardCharsets.UTF_8);
        ret = verify(sign, publicKey, data);

        return ret;
    }

    /**
     * 验签
     *
     * @param sign      签值
     * @param publicKey 公钥
     * @param data      业务报文字节数组
     * @return 验签成功返回true，反之为false
     * @throws Exception
     */
    public static boolean verify(String sign, String publicKey, byte[] data) throws Exception {
        if (data == null) {
            return false;
        }
        Signature signature = null;
        boolean flag = false;
        byte[] keyBytes = new Base64().decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        signature = Signature.getInstance(SIGNATURE_MD5);
        signature.initVerify(pubKey);
        signature.update(data);
        flag = signature.verify(new Base64().decode(sign));
        return flag;
    }


    /**
     * 不加密的排序方法
     * 取JSON到map
     *
     * @param jsonObj
     * @param map
     */
    public static void getNeedMap(JSONObject jsonObj, Map<String, String> map) {
        for (String key : jsonObj.keySet()) {
            Object object = jsonObj.get(key);
            if (object instanceof JSONObject) {
                getNeedMap(key, (JSONObject) object, map);
            } else if (object instanceof JSONArray) {
                JSONArray jsonArray = (JSONArray) object;
                for (Object arrayObject : jsonArray) {
                    if (arrayObject instanceof String) {
                        map.put((String) arrayObject, (String) arrayObject);
                    } else {
                        getNeedMap(key, (JSONObject) arrayObject, map);
                    }
                }
            } else if (object instanceof String) {
                map.put(key, (String) object);
            }
        }
    }

    /**
     * 不加密的排序方法
     * 取JSON到map
     */
    private static void getNeedMap(String parent, JSONObject jsonObj, Map<String, String> map) {
        for (String key : jsonObj.keySet()) {
            Object object = jsonObj.get(key);
            if (object instanceof JSONObject) {
                getNeedMap(parent.concat(key), (JSONObject) object, map);
            } else if (object instanceof JSONArray) {
                JSONArray jsonArray = (JSONArray) object;
                for (Object arrayObject : jsonArray) {
                    if (arrayObject instanceof String) {
                        map.put(parent.concat((String) arrayObject), (String) arrayObject);
                    } else {
                        getNeedMap(parent.concat(key), (JSONObject) arrayObject, map);
                    }
                }
            } else if (object instanceof String) {
                map.put(parent.concat(key), (String) object);
            }
        }
    }

    /**
     * 报文排序，使用 Map按key进行排序
     * 不加密的排序方法
     *
     * @param map
     * @return map
     */
    public static Map<String, String> sortMapByKey(Map<String, String> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map<String, String> sortMap = new TreeMap<String, String>(new Comparator<String>() {
            @Override
            public int compare(String str1, String str2) {
                return str1.compareTo(str2);
            }
        });
        sortMap.putAll(map);
        return sortMap;
    }

    /**
     * 获取加签字符串
     *
     * @param resultMap
     * @return
     * @Title: getSignString
     * @Description: TODO
     * @return: String
     */
    public static String getSignString(Map<String, String> resultMap) {
        // 需要加签的字符串
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : resultMap.entrySet()) {
            if (entry.getValue() != null && !"".equals(entry.getValue())) {
                builder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        return builder.toString();
    }

    /**
     * 签名
     *
     * @param data       待签名数据
     * @param privateKey 密钥
     * @return byte[] 数字签名
     */
    public static String sign(byte[] data, byte[] privateKey) throws Exception {
        // 取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        // 生成私钥
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        // 实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_MD5);
        // 初始化Signature
        signature.initSign(priKey);
        // 更新
        signature.update(data);
        return new Base64().encodeToString(signature.sign());
    }

    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     *
     * @param map 业务参数(含加密后的报文)
     * @return 拼接后的字符串
     */
    public static String createLinkString(Map<String, Object> map) {
        StringBuilder sBuilder = new StringBuilder();
        List<String> sortList = new ArrayList<String>(map.keySet());
        Collections.sort(sortList);
        for (int index = 0; index < sortList.size(); index++) {
            String key = sortList.get(index);
            String value = map.get(key).toString();
            if (value != null && !"".equals(value)) {
                sBuilder.append(sortList.get(index)).append("=").append(map.get(sortList.get(index)));
                if (index < sortList.size() - 1) {
                    sBuilder.append("&");
                }
            }
        }
        return sBuilder.toString();
    }

    /**
     * openabs渠道加签
     *
     * @param signStr    需要加签的字符串
     * @param privateKey 证书私钥
     * @return 加签后的字符串
     */
    public static String absSign(String signStr, String privateKey) throws Exception {
        byte[] data = signStr.getBytes(StandardCharsets.UTF_8);
        byte[] keyBytes = new Base64().decode(privateKey);
        //取得私钥
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //生成私钥对象
        PrivateKey pk = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(pk);
        signature.update(data);
        return new Base64().encodeToString(signature.sign());

    }

    /**
     * openabs渠道验签
     *
     * @param resultMap 响应报文
     * @param publicKey 公钥
     * @return
     */
    public static boolean absVerify(Map<String, Object> resultMap, String publicKey) throws Exception {
        String sign = (String) resultMap.get("sign");
        resultMap.remove("sign");
        byte[] data = null;
        boolean ret;
        Signature sig;
        data = createLinkString(resultMap).getBytes(StandardCharsets.UTF_8);
        sig = Signature.getInstance("SHA256withRSA");
        byte[] keyBytes = new Base64().decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        sig.initVerify(pubKey);
        sig.update(data);
        return sig.verify(new Base64().decode(sign));
    }

    public static final String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzNwubSseDqOueq04BlzK1HylM43ClimOL2/umsEGQQ9tk31YJxHjuT47qO3s/OxqQJ+r0umc77DwILnJtDhSYjJ8MGFfa00HLPUSVLv5gdNiVJM8cnTqJa4xB2n0Fnm7bNGWkxnf4qQIlA7PPJiNJLvSSYP0SUxnKNkDfJeKuWw3n67vpNI3gT65Ng8a98HzpulYU/b/cZq8aleJ7Zin+RkY433rr9FtEBypUz4qTPEu0je+MdxhiPRA4TA7tibXJgqP4sg8XXAms3GhScAwyL5PptvJGwYr0UOvieGUCzRDaNRmj2Rqns9td21rLZ0w4vifmMjvMOgmDCQAWc7i8wIDAQAB";

    public static void main(String[] args) throws Exception {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("randomKey", "em2i/dXt9/aQXfduIr8wrt3BjAf3SuawAJ1bM53csgtKOjrnNngSUzrspDcC2+decHBzRaLCNycQV73RW0eQ9fLwzmyBkHReC2UTUyV4HSfuCb+EfGmu96fIQwCGwJvlbTCGSYW3HUWGUuSQZ69h3d5SAlaNmjxccp/Ka+/ydRhwJsbiHdC4vZv+cCWu4+rAE5X3IseSacHKHHbq93bPlGtDwZr3wYmtMDLnBg3H33b1NJ2uciTnEz1zDHsNIqnKMr6aGiXT0fKEZO4o9dDEuYGnUDHPDO3CvIbXMTEJSWGqDGv0WhaQ/4RLQaeuaxVW5K0aSb4UyeI05oIlY4pOLw==");
        jsonObject.put("data", "lMn4iHevBYNgKhfxlYxcIcaNmiYvp559+oou939mnWjk1JtjmXlLOjmvf/gVEOGuDtkOXLVdky0aHfKKFCvJk7ehyScb+E6+5ptlFsr51Tfes4JgzrqRAJmrgq9hGvRK6BSDfoBiZ0rDYrKlYaAuzW6mpQCAb8XZYTD68duM2o4wXrKB1t6ecdMaACCOTGugulY/wmdJ3qQTddVxAI01BbJe1SWBethO9m/ej/eziNOorD8JOSDcLOOTrLYldFP0OLBq2OWY0yjEnNItn39l13TZa88zHnmBU97XQjycjnPm9ILYsLe8HqeOMgqqR830tr0HQCmH51NspR2iLsyjMXY2gIURm5QA8W3ZeMc0x0Mjpk2ubbqOyruY4fL4EsDfYdXaotzww4RiJQem/rzM9vuBw7lluiWx1KWmjb6wvW8TcQ49hr1ZxEuMhDKSvBzjd1tY0QWHNfYLpy+wjCzOS2GmRrv+veU5b//PZHHR90+ZJ5iYu+fLEH4ehAXrCM/f1x8N9Af1rRWC2jXrJPxXeBZfsi3pU9+zQkwnesXR5mhwhgHN5nqsKPSvWIHbCLfIio9i3ZvCYD2ZYpugxsGNdkPlRczPfij3jfjynHu7J3p/1nR0BS3etAomSqBSpbU2yiID8kPWEZVxVFWMILHTjA==");
        jsonObject.put("sign", "EAPvmonDCMwMhVrKoGOIFIlsaDt/wKuM0zQ6OQampg6z8w1sSW4GEpne0yQIzJezMTnr815SX+LyclLT2Gb5phc/+id9c0xy0c9dkO+IuN7Hz/8+5VzSpJMk2VOFEYbMFZ5s2ZW1mamn07HcBew6O8uHeExu6Rg4IGE0L5TzSRklXg82hI6hPoYf3pC4otFGd9jgERzTF9KmaIudoaCidNcCTc5M5a88IgJUJWa0gDQM1UTFeCxceSDBk1e1qX8j+uu7oD0HLnb519Tky5DV0gmOZToq/l2LEj6Gwn0/nQZ2hNMSH8/cW6sByeF8EUywwNlik7ECS5G3rFt+OWJ+8Q==");
        System.out.println(absVerify(jsonObject, publicKey));
    }
}
